package com.lhz.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.support.CronExpression;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

/**
 * cron表达式工具类
 *
 * @author LiGezZ
 */
@Slf4j
public class CronUtils {

    public static void main(String[] args) {
        String cronExpression = "0 0/30 * * * ?";
        boolean validExpression = CronExpression.isValidExpression(cronExpression);
        System.out.println("args = " + validExpression);
        CronExpression parse = CronExpression.parse(cronExpression);
        System.out.println("parse = " + parse);
        Object nextExecution = getNextExecution(cronExpression, 5, ZonedDateTime.now(), true);
        System.out.println("nextExecution = " + nextExecution);
        LocalDateTime dateTime = nextCurrentTime(cronExpression);
        System.out.println("dateTime = " + dateTime);
    }

    /**
     * 返回一个布尔值代表一个给定的Cron表达式的有效性
     *
     * @param cronExpression Cron表达式
     * @return boolean 表达式是否有效
     */
    public static boolean isValid(String cronExpression) {
        return CronExpression.isValidExpression(cronExpression);
    }

    /**
     * 返回下一个执行时间根据给定的Cron表达式
     *
     * @param cronExpression Cron表达式
     * @param num            获取数量
     * @param format         是否格式化为字符串
     * @return Date 下次Cron表达式执行时间
     */
    public static Object getNextExecution(String cronExpression, int num, ZonedDateTime zonedDateTime, boolean format) {

        List<String> dateStrList = new ArrayList<>();
        List<LocalDateTime> dateList = new ArrayList<>();
        try {
            CronExpression parse = CronExpression.parse(cronExpression);
            // 近n次时间
            for (int count = 1; count <= num; count++) {
                // 传入参数为计算下次时间点的开始时间
                zonedDateTime = parse.next(zonedDateTime);
                Instant instant = zonedDateTime.toInstant();
                ZoneId zoneId = ZoneId.systemDefault();
                LocalDateTime localDateTime = instant.atZone(zoneId).toLocalDateTime();
                //进行格式化
                if (format) {
                    DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                    String dateStr = zonedDateTime.format(fmt);
                    dateStrList.add(dateStr);
                    continue;
                }
                dateList.add(localDateTime);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        // 返回数据
        return format ? dateStrList : dateList;
    }


    /**
     * 与当前传入时间对比，如果时间差小于1s则获取第二个时间，
     * 主要为了解决时间周期很短的任务会出现返回的下次执行时间还在周期内(主要是1S的情况)
     *
     * @param cron
     * @return
     */
    public static LocalDateTime nextCurrentTime(String cron) {
        long timeMillis = System.currentTimeMillis();
        //如果存在获取的新的下次执行时间和当前已执行的时间一致，则返回第二个执行时间
        List<LocalDateTime> execution = (List<LocalDateTime>) getNextExecution(cron, 2, ZonedDateTime.now(), false);
        return execution.get(0);
    }
}
